#define _CRT_SECURE_NO_WARNINGS
#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_sInst;

// 从SpanList或者page cache获取一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	// 查看当前的spanlist中是否还有还未分配对象的span
	Span* it = list.Begin();
	while(it != list.End())
	{
		// 判断当前的Span还有没有挂着对象
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}

	// 先把central cache的桶锁解掉，这样如果其他线程释放内存对象回来，不会阻塞在这里
	list._mtx.unlock();


	// 走到这里（遍历完了）说明没有空闲的span了，那就再走下一层的page cache要
	PageCache::GetInstance()->GetMtx().lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	PageCache::GetInstance()->GetMtx().unlock();

	// 对获取到的span进行切分不需要加锁，因为这会其他线程访问不到这个span

	// 拿到span后还要切好和挂载好
	// 通过页号计算起始地址： 页号 << PAGE_SHIFT （设置的是每页为8k，其实就是页号 * 8 * 1024）
	// 这里用void* 不好控制，所以用char* 
	char* start = (char*)(span->_pageId << PAGE_SHIFT); // 计算span的大块内存的起始地址
	size_t bytes = span->_n << PAGE_SHIFT; // 计算span的大块内存的大小（字节数）
	char* end = start + bytes;

	// 把大块内存切成自由链表链接起来
	// 这里选择尾插，头插也可以，但是为了其物理地址空间的连续性着想所以选择尾插，因为链表中的物理地址空间不一定连续。
	// 地址连续起来其CPU的高速缓存利用率就比较高

	// 1、先切一块下来去做头，方便尾插
	span->_freeList = start;
	start += size;
	void* tail = span->_freeList;

	while (start < end)
	{
		NextObj(tail) = start;
		tail = NextObj(tail); // tail = start; 一样的，其实NextObj(tail)取头上的4/8byte 其实就是start
		start += size;
	}

	// 切好span以后，需要把span挂到桶里面去的时候，再把锁恢复一下
	list._mtx.lock(); 
	list.PushFront(span);

	return span;
}


// 从中心缓存获取一定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	// 1、先算一下你要的是哪个桶里的
	size_t index = SizeClass::Index(size);

	// 这里可能会遇到多个线程竞争同一个桶，所以要进行加锁
	_spanList[index]._mtx.lock();

	Span* span = GetOneSpan(_spanList[index], size);
	assert(span); // span不能为空
	assert(span->_freeList);

	start = span->_freeList;
	end = start;
	// 让end往后走batchNum-1步，然后更改指向即可取出batchNum个对象(但是要注意batchNum可能大于span中挂载的对象数量)
	// 所以不要for循环，改成while来判断
	
	/*for (size_t i = 0; i < batchNum - 1; ++i)
	{
		end = NextObj(end);
	}*/

	size_t i = 0;
	size_t actualNum = 1; // 实际到底获取了多少个
	while (i < batchNum - 1 && NextObj(end) != nullptr) // 不够的有几个给几个
	{
		end = NextObj(end);
		++i;
		++actualNum;
	}

	span->_freeList = NextObj(end);
	NextObj(end) = nullptr;

	_spanList[index]._mtx.unlock();


	return actualNum;
}


